// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_math.h"

namespace MK3DE
{
	BOOL IsPointInRect(D3DXVECTOR2* leftTop, D3DXVECTOR2* rightButtom, D3DXVECTOR2* point)
	{
		if (point->x >= leftTop->x && 
			point->x <= rightButtom->x &&
			point->y >= leftTop->y &&
			point->y <= rightButtom->y)
			return TRUE;
		return FALSE;
	}

	BOOL IsPointNearSegment(D3DXVECTOR2& p1, D3DXVECTOR2& p2, float x, float y, float rangeSq)
	{
		float disSq;
		if (abs(p1.x - p2.x) < MK_EPSILON)
			disSq = (p1.x - x) * (p1.x - x);
		else
		{
			float k = (p1.y - p2.y) / (p1.x - p2.x);
			float a = k;
			float b = -1.0f;
			float c = p1.y - k * p1.x;
			float up = a * x + b * y + c;
			disSq = up * up / (a * a + b * b);
		}

		if (disSq > rangeSq)
			return FALSE;

		float lenSq = D3DXVec2LengthSq(&D3DXVECTOR2(p1.x - p2.x, p1.y - p2.y));
		float t1 = D3DXVec2LengthSq(&D3DXVECTOR2(p1.x - x, p1.y - y));
		float t2 = D3DXVec2LengthSq(&D3DXVECTOR2(p2.x - x, p2.y - y));
		if (t1 > lenSq + rangeSq || t2 > lenSq + rangeSq)
			return FALSE;

		return TRUE;
	}

	BOOL AABBCollisionWithRay(D3DXVECTOR3& pos, AABB& aabb, float x0, float y0, float z0, D3DXVECTOR3& dir)
	{
		if (abs(dir.x) > MK_EPSILON)
		{
			// left.
			float t = (aabb.minPt.x - x0) / dir.x;
			if (t > 0)
			{
				float y = t * dir.y + y0;
				float z = t * dir.z + z0;

				if (y >= aabb.minPt.y && y <= aabb.maxPt.y && z >= aabb.minPt.z && z <= aabb.maxPt.z)
				{
					pos.x = aabb.minPt.x;
					pos.y = y;
					pos.z = z;
					return TRUE;
				}
			}

			// right.
			t = (aabb.maxPt.x - x0) / dir.x;
			if (t > 0)
			{
				float y = t * dir.y + y0;
				float z = t * dir.z + z0;

				if (y >= aabb.minPt.y && y <= aabb.maxPt.y && z >= aabb.minPt.z && z <= aabb.maxPt.z)
				{
					pos.x = aabb.minPt.x;
					pos.y = y;
					pos.z = z;
					return TRUE;
				}
			}
		}

		if (abs(dir.y) > MK_EPSILON)
		{
			// bottom.
			float t = (aabb.minPt.y - y0) / dir.y;
			if (t > 0)
			{
				float x = t * dir.x + x0;
				float z = t * dir.z + z0;

				if (x >= aabb.minPt.x && x <= aabb.maxPt.x && z >= aabb.minPt.z && z <= aabb.maxPt.z)
				{
					pos.x = x;
					pos.y = aabb.minPt.y;
					pos.z = z;
					return TRUE;
				}
			}

			// top.
			t = (aabb.maxPt.y - y0) / dir.y;
			if (t > 0)
			{
				float x = t * dir.x + x0;
				float z = t * dir.z + z0;

				if (x >= aabb.minPt.x && x <= aabb.maxPt.x && z >= aabb.minPt.z && z <= aabb.maxPt.z)
				{
					pos.x = x;
					pos.y = aabb.minPt.y;
					pos.z = z;
					return TRUE;
				}
			}
		}

		if (abs(dir.z) > MK_EPSILON)
		{
			// back.
			float t = (aabb.minPt.z - z0) / dir.z;
			if (t > 0)
			{
				float x = t * dir.x + x0;
				float y = t * dir.y + y0;

				if (x >= aabb.minPt.x && x <= aabb.maxPt.x && y >= aabb.minPt.y && y <= aabb.maxPt.y)
				{
					pos.x = x;
					pos.y = y;
					pos.z = aabb.minPt.z;
					return TRUE;
				}
			}

			// front.
			t = (aabb.maxPt.z - z0) / dir.z;
			if (t > 0)
			{
				float x = t * dir.x + x0;
				float y = t * dir.y + y0;

				if (x >= aabb.minPt.x && x <= aabb.maxPt.x && y >= aabb.minPt.y && y <= aabb.maxPt.y)
				{
					pos.x = x;
					pos.y = y;
					pos.z = aabb.minPt.z;
					return TRUE;
				}
			}
		}

		return FALSE;
	}

	BOOL RayIntersectTriangle(D3DXVECTOR3& pos, D3DXVECTOR3& orig, D3DXVECTOR3& dir, D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2)
	{
		D3DXVECTOR3 edge1 = v1 - v0;
		D3DXVECTOR3 edge2 = v2 - v0;

		D3DXVECTOR3 pvec;
		D3DXVec3Cross(&pvec, &edge1, &edge2);

		float dot = D3DXVec3Dot(&pvec, &dir);
		if (abs(dot) < MK_EPSILON)
			return FALSE;

		D3DXPLANE plane;
		D3DXPlaneFromPoints(&plane, &v0, &v1, &v2);

		float up = -plane.d - plane.a * orig.x - plane.b * orig.y - plane.c * orig.z;
		float down = plane.a * dir.x + plane.b * dir.y + plane.c * dir.z;
		float t = up / down;
		if (t <= MK_EPSILON)
			return FALSE;

		pos.x = dir.x * t + orig.x;
		pos.y = dir.y * t + orig.y;
		pos.z = dir.z * t + orig.z;

		D3DXVECTOR3 p0 = v0 - pos;
		D3DXVECTOR3 p1 = v1 - pos;
		D3DXVECTOR3 p2 = v2 - pos;

		D3DXVECTOR3 cp0, cp1, cp2;
		D3DXVec3Cross(&cp0, &p0, &p1);
		D3DXVec3Cross(&cp1, &p1, &p2);
		D3DXVec3Cross(&cp2, &p2, &p0);

		float d1 = D3DXVec3Dot(&cp0, &cp1);
		float d2 = D3DXVec3Dot(&cp0, &cp2);
		if (d1 > 0 && d2 > 0)
			return TRUE;

		return FALSE;
	}

	void TransformAABB(AABB* outAABB, AABB* inAABB, D3DXMATRIX* mat)
	{
		D3DXVECTOR3 pts[8];
		inAABB->Extract(pts, 8);

		outAABB->minPt = D3DXVECTOR3(3.3e33f, 3.3e33f, 3.3e33f);
		outAABB->maxPt = D3DXVECTOR3(-3.3e33f, -3.3e33f, -3.3e33f);

		for (int i = 0; i < 8; i++)
		{
			D3DXVECTOR3 tmp;
			D3DXVec3TransformCoord(&tmp, &pts[i], mat);
			outAABB->Merge(&tmp);
		}
	}

	int RandomInt(int nMin, int nMax)
	{
		if (nMin == nMax)
			return nMin;

		return (int)((float)rand() / RAND_MAX * (nMax - nMin) + nMin);
	}

	float RandomFloat(float fMin, float fMax)
	{
		if (abs(fMin - fMax) < MK_EPSILON)
			return fMin;

		return (float)rand() / (float)RAND_MAX * (fMax - fMin) + fMin;
	}

	void ProjectPoint(D3DXVECTOR3& outPos, D3DXVECTOR3& point, DWORD width, DWORD height, D3DXMATRIX& matView, D3DXMATRIX& matProj)
	{
		D3DXVECTOR3 pos(point);
		D3DXVec3TransformCoord(&pos, &pos, &matView);
		D3DXVec3TransformCoord(&pos, &pos, &matProj);
		outPos.x = (pos.x + 1.0f) * (float)width * 0.5f;
		outPos.y = (1.0f - pos.y) * (float)height * 0.5f;
		outPos.z = pos.z;
	}

	float GaussianDistribution1(float x, float rho)
	{
		float g = 1.0f / sqrtf(2.0f * D3DX_PI * rho * rho);
		g *= expf(-(x * x) / (2 * rho * rho));
		return g;
	}

	float GaussianDistribution2(float x, float y, float rho)
	{
		float g = 1.0f / sqrtf(2.0f * D3DX_PI * rho * rho);
		g *= expf(-(x * x + y * y) / (2 * rho * rho));
		return g;
	}

	void QuaternionFromYawPitchRoll(D3DXQUATERNION& q, float yaw, float pitch, float roll)
	{
		D3DXQuaternionRotationYawPitchRoll(&q, yaw, pitch, roll);
	}

	void QuaternionMultiply(D3DXQUATERNION& r, D3DXQUATERNION& q1, D3DXQUATERNION& q2)
	{
		D3DXQuaternionMultiply(&r, &q1, &q2);
	}

	BOOL TestPlanePoints(D3DXPLANE& plane, D3DXVECTOR3* pts, DWORD count)
	{
		for (DWORD i = 0; i < count; ++i)
			if (D3DXPlaneDotCoord(&plane, &pts[i]) > 0)
				return TRUE;

		return FALSE;
	}

	void PlaneIntersectLine(/* out */ D3DXVECTOR3* pOut, D3DXPLANE* pP, D3DXVECTOR3* pV1, D3DXVECTOR3* pV2)
	{
		D3DXPlaneIntersectLine(pOut, pP, pV1, pV2);
	}
}